#include <QtNetwork>
#include <QNetworkReply>
#include <delegate.h>
#include <algorithm>
#include <QList>
#include <QFile>
#include <QDebug>
#include <QMenuBar>
#include <QInputDialog>
#include <QLineEdit>
#include <QMessageBox>
#include <QFileDialog>

#include "controller.h"
#include <prioritydelegate.h>

Controller::Controller(Model* model, QWidget* parent) :
    QMainWindow(parent), model_(model)
{
    model_->setParent(this);
    sorting_model_ = new QSortFilterProxyModel(this);
    sorting_model_->setSourceModel(model_);
    sorting_model_->setDynamicSortFilter(true);

    cur_row_ = -1;
    cur_reply_ = NULL;
    view_ = new QTableView(this);

    view_->setModel(sorting_model_);
    view_->setSortingEnabled(true);
    view_->setItemDelegate(new Delegate(view_));
    view_->setItemDelegateForColumn(PRIORITY, new PriorityDelegate(view_));
    setCentralWidget(view_);

    add_task_action_ = new QAction("&Add task", this);
    connect(add_task_action_, SIGNAL(triggered()), SLOT(addTask()));

    file_menu_ = menuBar()->addMenu("&File");
    file_menu_->addAction(add_task_action_);

    manager_ = new QNetworkAccessManager();
    connect(model_, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
            SLOT(modelChanged(QModelIndex,QModelIndex)));
    connect(manager_, SIGNAL(finished(QNetworkReply*)),
                SLOT(replyFinished(QNetworkReply*)));

    //model_->add(Task(QUrl("http://cs319226.userapi.com/v319226188/ac9/-kWRV6_fDe4.jpg"), "/home/alex/1.jpg"));
}

void Controller::replyFinished(QNetworkReply *reply) {
    qDebug() << "FINISH";

    if (cur_reply_ == NULL) {
        qDebug() << "NULL";
        return;
     }


    qDebug() << "FINISH2";

    model_->setData(indexToCurrent(PROGRESS), 100);
    model_->setData(indexToCurrent(STATE), Task::FINISHED);

    QString path = model_->path(cur_row_);
    if (!saveFile(path, reply)) {
        model_->setData(indexToCurrent(STATE), Task::ERROR);
        model_->submitAll();
    }

    cur_reply_->deleteLater();
    cur_reply_ = NULL;

    cur_row_ = -1;

    startNext();
}

void Controller::downloadProgress(qint64 bytes_received, qint64 bytes_total) {
    int progress = double(bytes_received) / bytes_total * 100;
    if (progress > cur_progress_) { // to avoid excessive updates
        cur_progress_ = progress;
        model_->setData(indexToCurrent(PROGRESS), cur_progress_);
    }
}

void Controller::error(QNetworkReply::NetworkError code) {
    qDebug() << "Download error code: " << code;
    if (code == 5) { // interrupted by user
        return ;
    }
        model_->setData(indexToCurrent(STATE), Task::ERROR);
        manager_->disconnect();
        manager_->deleteLater();
        manager_ = new QNetworkAccessManager;
        connect(manager_, SIGNAL(finished(QNetworkReply*)),
                    SLOT(replyFinished(QNetworkReply*)));

    qDebug() << "deleting";
    cur_reply_->deleteLater();
    wipeCurrent();
    //startNext();
}

void Controller::sslErrors(const QList<QSslError> &errors) {
    model_->setData(indexToCurrent(STATE), Task::ERROR);
    wipeCurrent();
    startNext();
}

void Controller::modelChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) {
    if (topLeft.column() != PRIORITY) {
        return;
    }

    if (!isDownloading()) {
        return;
    }

    int row = findNext();
    if (row != -1 && model_->priority(cur_row_) < model_->priority(row)) {
        abortCurrent();
        startNext();
    }
}

void Controller::addTask() {
    bool status;
    QString url = QInputDialog::getText(this, "URL",
                                         "Enter URL:", QLineEdit::Normal,
                                         QString(), &status);
    if (!status || url.isEmpty())
        return;

    QString path = QFileDialog::getSaveFileName(this, "Saving");

    if (!status || path.isEmpty())
        return;

    qDebug() << "url: " << url << " file" << path;
    model_->add(Task(QUrl(url), path));
    int row = model_->rowCount() - 1;
    qDebug() << "url: " << model_->url(row);
    qDebug() << "path: " << model_->path(row);

    qDebug() << "cur: " << cur_reply_;
    if(cur_reply_ == 0) {
        startNext();
    }

}

void Controller::startNext() {
    int row = findNext();
    if (row != -1) { // there is unfinished task
        cur_progress_ = 0;
        cur_row_ = row;
        QUrl url = model_->url(row);
        if (!url.isValid()) {
           qDebug() << "bla";
        }
        cur_reply_ = manager_->get(QNetworkRequest(model_->url(row)));
        connect(cur_reply_, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(error(QNetworkReply::NetworkError)));
        connect(cur_reply_, SIGNAL(sslErrors(QList<QSslError>)), SLOT(sslErrors(QList<QSslError>)));
        connect(cur_reply_, SIGNAL(downloadProgress(qint64,qint64)), SLOT(downloadProgress(qint64,qint64)));
    }
}

void Controller::abortCurrent() {
    if (isDownloading()) {
        qDebug() << "abbort";
        cur_reply_->disconnect(this);
        QNetworkReply* reply = cur_reply_;
        wipeCurrent();
        qDebug() << 1;
        reply->abort();
        qDebug() << 2;
        reply->deleteLater();
    }
}

void Controller::wipeCurrent() {
        cur_row_ = -1;
        cur_reply_ = NULL;
        cur_progress_ = 0;
}

bool Controller::saveFile(QString path, QIODevice *device) {
    QFile file(path);
    if (!file.open(QIODevice::WriteOnly)) {
        fprintf(stderr, "Could not open %s for writing: %s\n",
                qPrintable(path),
                qPrintable(file.errorString()));
        return false;
    }

    file.write(device->readAll());
    file.close();

    return true;
}

QModelIndex Controller::indexToCurrent(TaskFields field) {
    if (cur_row_ < 0) {
        qDebug() << "INVALID CURRENT ROW: " << cur_row_;
    }
    return model_->index(cur_row_, field);
}

int Controller::findNext() {
    int row = -1;
    for (int i = 0; i < model_->rowCount(); ++i) {
        if (model_->state(i) == Task::REDY) {
            if (row == -1 || model_->priority(row) < model_->priority(i)) {
                row = i;
            }
        }
    }

    return row;
}

bool Controller::isDownloading() {
    return cur_reply_ != NULL && cur_reply_->isRunning();
}
